package br.com.localfloripa.entities;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

@Entity
@Table(name="usuario")
public class Usuario implements Serializable, UserDetails {
	
	private static final long serialVersionUID = -6923324609987416842L;
	private static final Logger log = LoggerFactory.getLogger(Usuario.class);
	
	@SequenceGenerator(name="Usuario_Seq",sequenceName="usuario_seq")
	@Id @GeneratedValue(generator="Usuario_Seq")
	private Long id;
	@Column(name="usuario",unique=true,nullable=false,length=60)	
	private String login;
	@Column(name="senha",nullable=false,length=20)
	private String senha;
	@Column(name="ativo",nullable=false)
	private Boolean ativo;
	@ManyToMany(fetch=FetchType.EAGER)
	@JoinTable(name="usuario_perfil", joinColumns={@JoinColumn(name="usuario_id")},inverseJoinColumns={@JoinColumn(name="perfil_id")})
	private List<Perfil> perfis = new ArrayList<Perfil>();	
	@OneToOne(optional = true,fetch=FetchType.EAGER)
	@JoinColumn(name="cliente_id")
	private Cliente cliente;
	
	public Usuario() {
		this.cliente = new Cliente();
	}
	
	@PrePersist
	private void prePersist(){
		this.ativo = true;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getLogin() {
		return login;
	}

	public void setLogin(String login) {
		this.login = login;
	}

	public String getSenha() {
		return senha;
	}

	public void setSenha(String senha) {
		this.senha = senha;
	}

	public Boolean getAtivo() {
		return ativo;
	}

	public void setAtivo(Boolean ativo) {
		this.ativo = ativo;
	}

	@Transient
	@Override
	public Collection<GrantedAuthority> getAuthorities() {
		
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		for (Perfil perfil : getPerfis()) {
			authorities.add(new GrantedAuthorityImpl(perfil.getAuthority()));
		}
		return authorities;
	}

	public List<Perfil> getPerfis() {
		return perfis;
	}

	public void setPerfis(List<Perfil> perfis) {
		this.perfis = perfis;
	}

	public Cliente getCliente() {
		return cliente;
	}

	public void setCliente(Cliente cliente) {
		this.cliente = cliente;
	}
	
	@Transient
	public String getNome() {
		ResourceBundle bundle = ResourceBundle.getBundle("messages.MessageBundle");
		return this.cliente != null ? this.cliente.getPessoa().getNome() : bundle.getString("usuario.title.admin"); 
	}
	
	@Transient
	public String getEmail() {
		ResourceBundle bundle = ResourceBundle.getBundle("messages.MessageBundle");
		return this.cliente != null ? this.cliente.getEmail() : bundle.getString("usuario.email.admin");
	}

	@Transient
	@Override
	public String getPassword() {
		return this.senha;
	}

	@Transient
	@Override
	public String getUsername() {
		return this.login;
	}

	@Transient
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Transient
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Transient
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Transient
	@Override
	public boolean isEnabled() {
		return this.ativo;
	}
	
	@Transient
	public static boolean hasRole(String role) {
		boolean hasRole = false;
		UserDetails userDetails = getUserDetails();
		if (userDetails != null) {
			Collection<GrantedAuthority> authorities = userDetails.getAuthorities();
			if (isRolePresent(authorities, role)) {
				hasRole = true;
			}
		} 
		return hasRole;
	}
	
	/**
	 * Get info about currently logged in user
	 * @return UserDetails if found in the context, null otherwise
	 */
	@Transient
	private static UserDetails getUserDetails() {
		UserDetails userDetails = null;
		SecurityContext context = SecurityContextHolder.getContext();
		if (context != null) {			
			Authentication auth = context.getAuthentication();
			if (auth != null) {
				Object principal = auth.getPrincipal();
				if (principal instanceof UserDetails) {
					userDetails = (UserDetails) principal;
				}
			}			
		}
		return userDetails;
	}
	
	@Transient
	public static Boolean isAuthenticated() {
		
		boolean authenticated = false;
		SecurityContext context = SecurityContextHolder.getContext();
		if (context != null) {			
			Authentication auth = context.getAuthentication();
			if (auth != null) {
				authenticated = auth.isAuthenticated();
			}
		}
		return authenticated;		
	}
	
	/**
	 * Check if a role is present in the authorities of current user
	 * @param authorities all authorities assigned to current user
	 * @param role required authority
	 * @return true if role is present in list of authorities assigned to current user, false otherwise
	 */
	@Transient
	private static boolean isRolePresent(Collection<GrantedAuthority> authorities, String role) {
		boolean isRolePresent = false;
		for (GrantedAuthority grantedAuthority : authorities) {
			isRolePresent = grantedAuthority.getAuthority().equals(role);
			if (isRolePresent) break;
		}
		return isRolePresent;
	}
	
	@Transient
	public static Usuario getLoggedUser() {		
		return (Usuario) getUserDetails();
	}

}
